﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * CLR版本：4.0.30319.42000
 * 文件名：Kernel
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/9 18:49:49
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms.VisualStyles;
using ImageK.Gui;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace ImageK
{
    /// <summary>
    /// This class installs and updates ImageJ's menus. Note that menu labels,
    /// even in submenus, must be unique.This is because ImageJ uses a single
    /// hash table for all menu labels.If you look closely, you will see that
    /// File->Import->Text Image...and File->Save As->Text Image... do not use
    /// the same label.One of the labels has an extra space.
    /// </summary>
    public class Menus
    {
        public static char PLUGINS_MENU = 'p';
        public static char IMPORT_MENU = 'i';
        public static char SAVE_AS_MENU = 's';
        public static char SHORTCUTS_MENU = 'h'; // 'h'=hotkey
        public static char ABOUT_MENU = 'a';
        public static char FILTERS_MENU = 'f';
        public static char TOOLS_MENU = 't';
        public static char UTILITIES_MENU = 'u';

        public static int WINDOW_MENU_ITEMS = 6; // fixed items at top of Window menu

        public static int NORMAL_RETURN = 0;
        public static int COMMAND_IN_USE = -1;
        public static int INVALID_SHORTCUT = -2;
        public static int SHORTCUT_IN_USE = -3;
        public static int NOT_INSTALLED = -4;
        public static int COMMAND_NOT_FOUND = -5;

        public static int MAX_OPEN_RECENT_ITEMS = 15;

        private static Menus instance;
        private static MenuStrip mbar;

        // private static CheckboxMenuItem gray8Item, gray16Item, gray32Item,
        //     color256Item, colorRGBItem, RGBStackItem, HSBStackItem, LabStackItem, HSB32Item;
        // private static PopupMenu popup;

        private static ImageK ij;

        private static string ImageJPath, pluginsPath, macrosPath;

        private static Dictionary<string, object> menus;
        private static Dictionary<string, object> menuSeparators;
        private static ToolStripMenuItem pluginsMenu, saveAsMenu, shortcutsMenu, utilitiesMenu, macrosMenu;
        public static ToolStripMenuItem window, openRecentMenu;
        private static Dictionary<string, object> _pluginsTable;
        private static int nPlugins, nMacros;
        private static Dictionary<Keys,string> shortcuts;

        private static Dictionary<Keys, string> macroShortcuts;
        private static ArrayList pluginsPrefs; // commands saved in IJ_Prefs
        static int windowMenuItems2; // non-image windows listed in Window menu + separator
        private string error;
        private string jarError;
        private string pluginError;
        private bool isJarErrorHeading;
        private static bool installingJars, duplicateCommand;
        private static ArrayList jarFiles;  // JAR files in plugins folder with "_" in their name
        private Hashtable menuEntry2jarFile = new Hashtable();
        private static ArrayList macroFiles;  // Macros and scripts in the plugins folder
        private static int userPluginsIndex; // First user plugin or submenu in Plugins menu
        private static bool addSorted;

        private static int defaultFontSize = IJ.IsWindows() ? 15 : 0;
        private static int fontSize;
        private static double scale = 1.0;
        private static Font cachedFont;

        static bool jnlp; // true when using Java WebStart
        public static int setMenuBarCount;

        

        public Menus(ImageK ijInstance)
        {
            ij = ijInstance;
            string title = ij != null ? ij.Text : null;
            instance = this;
            fontSize = Prefs.getInt(Prefs.MENU_SIZE, defaultFontSize);
        }

        public string addMenuBar()
        {
            //todo:
            mbar = null;
            menus = new Dictionary<string, object>();
            _pluginsTable = new Dictionary<string, object>();
            shortcuts = new Dictionary<Keys, string>();

            ToolStripMenuItem file = getMenu("File");
            getMenu("File>New", true);

            addPlugInItem(file, "Open...", "ImageK.Plugin.Commands(\"open\")", Keys.O, false);
            addOpenRecentSubMenu(file);
            file.DropDownItems.Add(new ToolStripSeparator());
            addPlugInItem(file, "Close", "ImageK.Plugin.Commands(\"close\")", Keys.W, false);
            addPlugInItem(file, "Close All", "ImageK.Plugin.Commands(\"close-all\")", Keys.W, true);
            addPlugInItem(file, "Save", "ImageK.Plugin.Commands(\"save\")", Keys.S, false);
            saveAsMenu = getMenu("File>Save As", true);

            ToolStripMenuItem edit = getMenu("Edit");
            addPlugInItem(edit, "Undo", "ImageK.Plugin.Commands(\"undo\")", Keys.Z, false);
            edit.DropDownItems.Add(new ToolStripSeparator());
            addPlugInItem(edit, "Cut", "ImageK.Plugin.Clipboard(\"cut\")", Keys.X, false);
            addPlugInItem(edit, "Copy", "ImageK.Plugin.Clipboard(\"copy\")", Keys.C, false);
            // addPlugInItem(edit, "Copy to System", "ij.plugin.Clipboard(\"scopy\")", 0, false);
            addPlugInItem(edit, "Paste", "ImageK.Plugin.Clipboard(\"paste\")", Keys.V, false);
            // addPlugInItem(edit, "Paste Control...", "ij.plugin.frame.PasteController", 0, false);

            edit.DropDownItems.Add(new ToolStripSeparator());
            addPlugInItem(edit, "Clear", "ImageK.Plugin.Filter.Filler(\"clear\")", 0, false);
            // addPlugInItem(edit, "Clear Outside", "ij.plugin.filter.Filler(\"outside\")", 0, false);
            addPlugInItem(edit, "Fill", "ImageK.Plugin.Filter.Filler(\"fill\")", Keys.F, false);
            addPlugInItem(edit, "Draw", "ImageK.Plugin.Filter.Filler(\"draw\")", Keys.D, false);
            addPlugInItem(edit, "Invert", "ImageK.Plugin.Filter.Filters(\"invert\")", Keys.I, true);
            edit.DropDownItems.Add(new ToolStripSeparator());
            getMenu("Edit>Selection", true);
            ToolStripMenuItem optionsMenu = getMenu("Edit>Options", true);

            ToolStripMenuItem image = getMenu("Image");
            // Plugins installed in the Image/ Adjust submenu
            getMenu("Image>Adjust", true);
            addPlugInItem(image, "Show Info...", "ImageK.Plugin.ImageInfo", Keys.I, false);

            getMenu("Image>Color", true);
            getMenu("Image>Stacks", true);

            addPlugInItem(image, "Scale...", "ImageK.Plugin.Scaler", Keys.E, false);

            // Plugins installed in the Image/ Transform submenu
            getMenu("Image>Transform", true);

            image.DropDownItems.Add(new ToolStripSeparator());
            getMenu("Image>Lookup Tables", true);

            ToolStripMenuItem process = getMenu("Process");
            addPlugInItem(process, "Smooth", "ImageK.Plugin.Filter.Filters(\"smooth\")", Keys.S, true);
            addPlugInItem(process, "Sharpen", "ImageK.Plugin.Filter.Filters(\"sharpen\")", 0, false);
            addPlugInItem(process, "Find Edges", "ImageK.Plugin.Filter.Filters(\"edge\")", 0, false);

            getMenu("Process>Binary", true);


            ToolStripMenuItem analyzeMenu = getMenu("Analyze");
            addPlugInItem(analyzeMenu, "Measure", "ImageK.Plugin.Filter.Analyzer", Keys.M, false);

            addPlugInItem(analyzeMenu, "Set Measurements...", "ImageK.Plugin.Filter.Analyzer(\"set\")", 0, false);
            image.DropDownItems.Add(new ToolStripSeparator());
            addPlugInItem(analyzeMenu, "Histogram", "ImageK.Plugin.Histogram", Keys.H, false);
            addPlugInItem(analyzeMenu, "Plot Profile", "ImageK.Plugin.Profiler(\"plot\")", Keys.K, false);
            getMenu("Analyze>Tools", true);

            // the plugins will be added later, after a separator
            addPluginsMenu();

            // make	sure "Quit" is the last item in the File menu
            file.DropDownItems.Add(new ToolStripSeparator());
            addPlugInItem(file, "Quit", "ImageK.Plugin.Commands(\"quit\")", 0, false);

            if (fontSize != 0 || scale > 1.0)
            {
                //todo:
            }
            if (ij != null)
            {
                ij.Controls.Add(mbar);
                Menus.setMenuBarCount++;
            }

            return "";
        }

        private static ToolStripMenuItem getMenu(string menuPath)
        {
            return getMenu(menuPath, false);
        }

        private static ToolStripMenuItem getMenu(string menuName, bool readFromProps)
        {
            if (menuName.EndsWith(">"))
                menuName = menuName.Substring(0, menuName.Length - 1);
            ToolStripMenuItem result;
            if (!menus.ContainsKey(menuName))
            {
                int offset = menuName.LastIndexOf('>');
                if (offset < 0)
                {
                    //todo:
                    result = new ToolStripMenuItem(menuName);
                    if (mbar == null)
                        mbar = new MenuStrip();
                    if (menuName.Equals("Help"))
                    {
                        //todo:
                        // mbar.setHelpMenu(result);
                    }
                    else
                        mbar.Items.Add(result);
                    if (menuName.Equals("Window"))
                        window = result;
                    else if (menuName.Equals("Plugins"))
                        pluginsMenu = result;
                }
                else
                {
                    string parentName = menuName.Substring(0, offset);
                    string menuItemName = menuName.Substring(offset + 1);
                    ToolStripMenuItem parentMenu = getMenu(parentName);
                    result = new ToolStripMenuItem(menuItemName);
                    // addPluginSeparatorIfNeeded(parentMenu);
                    if (readFromProps)
                        result = addSubMenu(parentMenu, menuItemName);
                    else if (parentName.StartsWith("Plugins") && menuSeparators != null)
                    {
                        //todo:
                        // addItemSorted(parentMenu, result, parentName.Equals("Plugins") ? userPluginsIndex : 0);
                    }
                    else
                        parentMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[]{result});
                    if (menuName.Equals("File>Open Recent"))
                        openRecentMenu = result;
                }
                menus.Add(menuName, result);
            }
            else
            {
                result = (ToolStripMenuItem)menus[menuName];
            }
            //System.out.println("menuName: "+menuName);
            //todo:
            // if (IJ.IsWindows() && menuName != null && menuName.Contains(">"))
            //     fixFontSize(result);
            return result;
        }


        static ToolStripMenuItem addSubMenu(ToolStripMenuItem menu, string name)
        {
            string value;
            string key = name.ToLower();
            int index;
            ToolStripMenuItem submenu = new ToolStripMenuItem(name.Replace('_', ' '));
            index = key.IndexOf(' ');
            if (index > 0)
                key = key.Substring(0, index);
            for (int count = 1; count < 100; count++)
            {
                value = Prefs.getString(key + (count / 10) % 10 + count % 10);
                if (value == null)
                    break;
                if (count == 1)
                {
                    menu.DropDownItems.AddRange(new ToolStripItem[]{submenu});
                }
                if (value.Equals("-"))
                {
                    submenu.DropDownItems.Add(new ToolStripSeparator());
                }
                else
                {
                    addPluginItem(submenu, value);
                }
            }
            //todo:
            // if (name.Equals("Lookup Tables") && applet == null)
            //     addLuts(submenu);
            // fixFontSize(submenu);
            return submenu;
        }

        void addOpenRecentSubMenu(ToolStripMenuItem menu)
        {
            openRecentMenu = getMenu("File>Open Recent");
            for (int i = 0; i<MAX_OPEN_RECENT_ITEMS; i++)
            {
                string path = Prefs.getString("recent" + (i/10)%10 + i%10);
                if (path==null) break;
                ToolStripMenuItem item = new ToolStripMenuItem(path);
                openRecentMenu.DropDownItems.Add(item);
                item.Click+= MenuItemOnClick;
            }
            menu.DropDownItems.Add(openRecentMenu);
        }

        static void addItem(ToolStripMenuItem menu, string label, Keys shortcut, bool shift)
        {
            if (menu == null)
                return;
            ToolStripMenuItem item;
            if (shortcut == Keys.None)
                item = new ToolStripMenuItem(label);
            else
            {
                if (shift)
                {
                    item = new ToolStripMenuItem(label);
                    shortcuts.Add(shortcut | Keys.Shift, label);
                }
                else
                {
                    item = new ToolStripMenuItem(label);
                    shortcuts.Add(shortcut, label);
                }
            }
            // if (addSorted)
            // {
            //     if (menu == pluginsMenu)
            //         addItemSorted(menu, item, userPluginsIndex);
            //     else
            //         addOrdered(menu, item);
            // }
            // else
            {
                menu.DropDownItems.Add(item);
            }
                
            item.Click += MenuItemOnClick;
            // fixFontSize(item);
        }

        private static void MenuItemOnClick(object? sender, EventArgs e)
        {
            ij?.OnMenuItemClicked(sender, e);
        }

        void addPlugInItem(ToolStripMenuItem menu, string label, string className, Keys shortcut, bool shift)
        {
            _pluginsTable.Add(label, className);
            nPlugins++;
            addItem(menu, label, shortcut, shift);
        }

        static void addPluginItem(ToolStripMenuItem submenu, string s)
        {
            if (s.StartsWith("\"-\""))
            {
                // add menu separator if command="-"
                addSeparator(submenu);
                return;
            }
            int lastComma = s.LastIndexOf(',');
            if (lastComma <= 0)
                return;
            string command = s.Substring(1, lastComma - 2);
            int keyCode = 0;
            Keys tmpKey = Keys.None;
            if (!Enum.TryParse<Keys>(keyCode.ToString(), true, out tmpKey))
            {
                tmpKey = Keys.None;
            }
            bool shift = false;
            if (command.EndsWith("]"))
            {
                int openBracket = command.LastIndexOf('[');
                if (openBracket > 0)
                {
                    var tmpLen = (command.Length - 1) - (openBracket + 1);
                    string shortcut = command.Substring(openBracket + 1, tmpLen);
                    //todo:
                    // keyCode = convertShortcutToCode(shortcut);
                    // boolean functionKey = keyCode >= KeyEvent.VK_F1 && keyCode <= KeyEvent.VK_F12;
                    // if (keyCode > 0 && !functionKey)
                    //     command = command.substring(0, openBracket);
                }
            }
            if (keyCode >= (int)Keys.F1 && keyCode <= (int)Keys.F12)
            {
                
                shortcuts.Add(tmpKey, command);
                keyCode = 0;
            }
            else if (keyCode >= 265 && keyCode <= 290)
            {
                keyCode -= 200;
                shift = true;
            }
            addItem(submenu, command, tmpKey, shift);
            while (s[lastComma + 1] == ' ' && lastComma + 2 < s.Length)
                lastComma++; // remove leading spaces
            string className = s.Substring(lastComma + 1);
            //IJ.log(command+"  "+className);
            // if (installingJars)
            //     duplicateCommand = pluginsTable.get(command) != null;
            _pluginsTable.Add(command, className);
            nPlugins++;
        }

        void addPluginsMenu()
        {
            String value, label, className;
            int index;
            //pluginsMenu = new Menu("Plugins");
            pluginsMenu = getMenu("Plugins");
            for (int count = 1; count<100; count++)
            {
                value = Prefs.getString("plug-in" + (count/10)%10 + count%10);
                if (value==null)
                    break;
                char firstChar = value[0];
                if (firstChar=='-')
                    pluginsMenu.DropDownItems.Add(new ToolStripSeparator());
                else if (firstChar=='>')
                {
                    string submenu = value.Substring(2, value.Length-3);
                    //Menu menu = getMenu("Plugins>" + submenu, true);
                    ToolStripMenuItem menu = addSubMenu(pluginsMenu, submenu);
                    if (submenu.Equals("Shortcuts"))
                        shortcutsMenu = menu;
                    else if (submenu.Equals("Utilities"))
                        utilitiesMenu = menu;
                    else if (submenu.Equals("Macros"))
                        macrosMenu = menu;
                }
                else
                    addPluginItem(pluginsMenu, value);
            }
            userPluginsIndex = pluginsMenu.DropDownItems.Count;
            if (userPluginsIndex<0) userPluginsIndex = 0;
        }

        static void addSeparator(ToolStripMenuItem menu)
        {
            menu.DropDownItems.Add(new ToolStripSeparator());
        }

        /** Adds one image to the end of the Window menu. */
        public static void addWindowMenuItem(ImagePlus imp)
        {
            //todo:

            // if (ij == null)
            //     return;
            // string name = imp.getTitle();
            // string size = ImageWindow.getImageSize(imp);
            // CheckboxMenuItem item = new CheckboxMenuItem(name + " " + size);
            // item.setActionCommand("" + imp.getID());
            // window.add(item);
            // fixFontSize(item);
            // item.addItemListener(ij);
        }

        /** Changes the name of an item in the Window menu. */
        public static void updateWindowMenuItem(string oldLabel, string newLabel)
        {
            updateWindowMenuItem(null, oldLabel, newLabel);
        }

        /** Changes the name of an item in the Window menu. */
        public static void updateWindowMenuItem(ImagePlus imp, string oldLabel, string newLabel)
        {
            //todo:

            // if (oldLabel == null || newLabel == null)
            //     return;
            // int first = WINDOW_MENU_ITEMS;
            // int count = window.getItemCount();
            // try
            // {  // workaround for Linux/Java 5.0/bug
            //     for (int i = first; i < count; i++)
            //     {
            //         MenuItem item = window.getItem(i);
            //         String label = item.getLabel();
            //         String cmd = item.getActionCommand();
            //         if (imp != null)
            //         {  //remove size (e.g. " 24MB")
            //             int index = label.lastIndexOf(" ");
            //             if (index > -1)
            //                 label = label.substring(0, index);
            //         }
            //         if (item != null && label.equals(oldLabel) && (imp == null || ("" + imp.getID()).equals(cmd)))
            //         {
            //             String size = "";
            //             if (imp != null)
            //                 size = " " + ImageWindow.getImageSize(imp);
            //             item.setLabel(newLabel + size);
            //             return;
            //         }
            //     }
            // }
            // catch (Exception e) { }
        }

        public static ToolStripMenuItem getOpenRecentMenu()
        {
            return openRecentMenu;
        }

        public int getMacroCount()
        {
            return nMacros;
        }

        public int getPluginCount()
        {
            return nPlugins;
        }

        static int RGB_STACK = 10, HSB_STACK = 11, LAB_STACK = 12, HSB32_STACK = 13;

        /// <summary>
        /// Updates the Image/Type and Window menus.
        /// </summary>
        public static void updateMenus()
        {
            //todo:
        }

        /** Use Prefs.getImageJDir() to get the path to the ImageJ directory. */
        public static String getImageJPath()
        {
            return ImageJPath;
        }

        /** Returns the path to the user plugins directory or
            null if the plugins directory was not found. */
        public static String getPlugInsPath()
        {
            return pluginsPath;
        }

        /** Returns the path to the macros directory or
            null if the macros directory was not found. */
        public static String getMacrosPath()
        {
            return macrosPath;
        }

        /** Returns the hashtable that associates commands with plugins. */
        public static Dictionary<string, object> getCommands()
        {
            // if (_pluginsTable == null)
            //     IJ.Init();
            return _pluginsTable;
        }

        /** Returns the hashtable that associates shortcuts with commands. The keys
        in the hashtable are Integer keycodes, or keycode+200 for uppercase. */
        public static Dictionary<Keys, string> getShortcuts()
        {
            return shortcuts;
        }

        /** Called once when ImageJ quits. */
        public static void savePreferences(Dictionary<string,string> prefs)
        {
            //todo:
            // if (pluginsPrefs==null)
            //     return;
            // int index = 0;
            // for (Enumeration en = pluginsPrefs.elements(); en.hasMoreElements();)
            // {
            //     String key = "plugin" + (index/10)%10 + index%10;
            //     String value = (String)en.nextElement();
            //     prefs.put(key, value);
            //     index++;
            // }
            int n = openRecentMenu.DropDownItems.Count;
            for (int i = 0; i<n; i++)
            {
                string key = ""+i;
                if (key.Length==1) key = "0"+key;
                key = "recent"+key;
                prefs[key] = openRecentMenu.DropDownItems[i].Text;
            }
            prefs[Prefs.MENU_SIZE] = fontSize.ToString();
        }

        /** Adds a file path to the beginning of the File/Open Recent submenu. */
        public static void addOpenRecentItem(string path)
        {
            if (ij==null) return;
            int count = openRecentMenu.DropDownItems.Count;
            for (int i = 0; i<count;)
            {
                if (openRecentMenu.DropDownItems[i].Text.Equals(path))
                {
                    openRecentMenu.DropDownItems.RemoveAt(i);
                    count--;
                }
                else
                    i++;
            }
            if (count==MAX_OPEN_RECENT_ITEMS)
                openRecentMenu.DropDownItems.RemoveAt(MAX_OPEN_RECENT_ITEMS-1);
            ToolStripMenuItem item = new ToolStripMenuItem(path);
            openRecentMenu.DropDownItems.Insert(0, item);
            item.Click += MenuItemOnClick;
        }


        // public static Font getCachedFont()
        // {
        //     if (cachedFont==null)
        //         cachedFont = getFont(false);
        //     return cachedFont;
        // }

        /** Called once when ImageJ quits. */
        // public static void savePreferences(Properties prefs)
        // {
        //     if (pluginsPrefs==null)
        //         return;
        //     int index = 0;
        //     for (Enumeration en = pluginsPrefs.elements(); en.hasMoreElements();)
        //     {
        //         String key = "plugin" + (index/10)%10 + index%10;
        //         String value = (String)en.nextElement();
        //         prefs.put(key, value);
        //         index++;
        //     }
        //     int n = openRecentMenu.getItemCount();
        //     for (int i = 0; i<n; i++)
        //     {
        //         String key = ""+i;
        //         if (key.length()==1) key = "0"+key;
        //         key = "recent"+key;
        //         prefs.put(key, openRecentMenu.getItem(i).getLabel());
        //     }
        //     prefs.put(Prefs.MENU_SIZE, Integer.toString(fontSize));
        // }
        //
        // public static void updateImageJMenus()
        // {
        //     jarFiles = macroFiles = null;
        //     Menus m = new Menus(IJ.GetInstance(), IJ.getApplet());
        //     String err = m.addMenuBar();
        //     if (err!=null) IJ.error(err);
        //     m.installStartupMacroSet();
        //     IJ.resetClassLoader();
        //     //IJ.runPlugIn("ij.plugin.ClassChecker", "");
        //     IJ.ShowStatus("Menus updated: "+m.nPlugins + " commands, " + m.nMacros + " macros");
        // }
        //
        // public static void updateFont()
        // {
        //     scale = (int)Math.round(Prefs.getGuiScale());
        //     Font font = getFont();
        //     mbar.setFont(font);
        //     if (ij!=null)
        //         ij.setMenuBar(mbar);
        //     popup.setFont(font);
        // }

        /** Adds a command to the ImageJ menu bar. */
        public static void add(String menuPath, String plugin)
        {
            if (_pluginsTable==null)
                return;
            int index = menuPath.LastIndexOf(">");
            if (index==-1 || index==menuPath.Length-1)
                return;
            String label = menuPath.Substring(index+1, menuPath.Length);
            menuPath = menuPath.Substring(0, index);
            _pluginsTable.Add(label, plugin);
            addItem(getMenu(menuPath), label, 0, false);
        }

        /** Work around Windows bug that limits menu bar sub-menu to 17 points. */
        // private static void fixFontSize(ToolStripMenuItem item)
        // {
        //     if (IJ.IsWindows() && item!=null)
        //         item.Font = getCachedFont();
        // }
    }
}
